﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Hydra.Shell.Interfaces;
using System.Collections.Generic;
using System.Reflection;
using System.Diagnostics;
using SLControlLibrary;

namespace Hydra.Shell.Exceptions
{
    public class ServiceAvailableButRequiresDependenciesException : Exception
    {
        public IQueryServiceProvider QueryServiceProvider { get; private set; }
        public List<Type> Dependencies { get; private set; }
        public ConstructorInfo Constructor { get; private set; }
        private IPackageAgent packageAgent;

        public ServiceAvailableButRequiresDependenciesException()
        {
        }

        public ServiceAvailableButRequiresDependenciesException(IPackageAgent packageAgent, IQueryServiceProvider queryServiceProvider, List<Type> dependencies, ConstructorInfo constructor)
        {
            this.packageAgent = packageAgent;

            this.QueryServiceProvider = queryServiceProvider;
            this.Dependencies = dependencies;
            this.Constructor = constructor;
        }

        public ServiceLoadOperation<T> Resolve<T>(UIThread uiThread) where T : IUnknown
        {
            var constructor = this.Constructor;
            var dependencies = this.Dependencies;
            var serviceProvider = this.QueryServiceProvider;
            var parameters = new object[dependencies.Count + 1];
            var dependencyOps = new Dictionary<int, ServiceLoadOperation>();
            var index = 1;
            var count = 0;
            ServiceLoadOperation<T> serviceLoadOperation;

            serviceLoadOperation = new ServiceLoadOperation<T>(uiThread);

            count = dependencies.Count;

            packageAgent.SendGeneralMessage("Required dependency count = " + count.ToString());

            foreach (var type in dependencies)
            {
                ServiceLoadOperation serviceLoadOp;

                packageAgent.SendGeneralMessage("Required dependency query for " + type.Name);

                if (serviceProvider.QueryService(type.GetGuid(), type.GetGuid(), out serviceLoadOp))
                {
                    var thisIndex = index;

                    dependencyOps.Add(index, serviceLoadOp);

                    serviceLoadOp.ExceptionOccured += (sender, e) =>
                    {
                        Debugger.Break();
                    };

                    serviceLoadOp.ServiceLoaded += (sender, e) =>
                    {
                        parameters[thisIndex] = e.Service;
                        dependencyOps.Remove(thisIndex);

                        packageAgent.SendGeneralMessage(string.Format("Required dependency {0} downloaded. {1} remaining.", type.Name, dependencyOps.Count));

                        if (dependencyOps.Count == 0)
                        {
                            T interfaceObject;

                            parameters[0] = uiThread;

                            var service = (IUnknown)constructor.Invoke(parameters);

                            if (service.QueryInterface<T>(out interfaceObject))
                            {
                                packageAgent.SendGeneralMessage("All dependencies downloaded.");

                                serviceLoadOperation.DelaySetLoaded(interfaceObject);
                            }
                            else
                            {
                                Debugger.Break();
                            }
                        }
                    };
                }
                else
                {
                    Debugger.Break();
                }

                index++;
            }

            return serviceLoadOperation;
        }

        public override string Message
        {
            get
            {
                return "Service available but requires dependencies";
            }
        }    
    }
}
